package ink.xiaobaibai.multipleClick;

import ink.xiaobaibai.common.MySecurity;
import ink.xiaobaibai.response.ResponseFormat;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @description: 禁止多次点击aop
 * @author: 小白白
 * @create: 2021-05-22
 **/

@Aspect
@Component
@Slf4j
public class MultipleClickAroundAop {

    /**
     * client专属
     */

    private static final String MULTIPLE_CLICK_KEY = "click:";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Pointcut(value = "@annotation(ink.xiaobaibai.multipleClick.ForbidMultipleClick)")
    public void clickPointcut() {

    }

    @Around(value = "clickPointcut()")
    public Object clickAround(ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        ForbidMultipleClick annotation = method.getAnnotation(ForbidMultipleClick.class);
        int seconds = annotation.durationSecondsTime();
        String msg = annotation.msg();
        String anKey = annotation.key();
        String key;
        if (anKey.length() == 0) {
            //默认userId
            key = MULTIPLE_CLICK_KEY + MySecurity.getUserId();
        } else {
            //否则就解析
            key = MULTIPLE_CLICK_KEY + this.analysis(method, joinPoint.getArgs(), anKey);
        }
        if (annotation.prefixUserId()) {
            key = MySecurity.getUserId() + key;
        }
        if (!this.stringRedisTemplate.opsForValue().setIfAbsent(key, "1", seconds, TimeUnit.SECONDS)) {
            //不存在值会返回true,存在值会返回false
            return ResponseFormat.fail(msg + ",请" + seconds + "秒后再试");
        }
        Object proceed;
        try {
            proceed = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            log.error("严重错误" + throwable.getMessage());
            return ResponseFormat.fail("系统繁忙");
        }
        return proceed;
    }

    /**
     * spel表达式解析器
     */
    private SpelExpressionParser spelExpressionParser = new SpelExpressionParser();

    /**
     * 参数名发现器
     */
    private DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    /**
     * 返回参数名
     */
    private String[] discovererMethodParams(Method method) {
        return this.parameterNameDiscoverer.getParameterNames(method);
    }

    /**
     * 开始解析 将注解里的SpEL表达式转为目标值
     */
    private String analysis(Method method, Object[] args, String key) {
        EvaluationContext context = new StandardEvaluationContext();
        String[] params = this.discovererMethodParams(method);
        for (int i = 0; i < args.length; i++) {
            //形参名称-实际的值
            context.setVariable(params[i], args[i]);
        }
        return Objects.requireNonNull(this.spelExpressionParser.parseExpression(key).getValue(context)).toString();
    }

}
